package main

import (
	"encoding/json"
	"fmt"
)

//option 设计模式
//Go创建对象时，如何优雅的传递初始化参数？这里所说的优雅，指的是：
//
//支持传递多个参数
//参数个数、类型发生变化时，尽量保持接口的兼容性
//参数支持默认值
//具体的参数可根据调用方需关心的程度，决定是否提供默认值
//Go并不像c++和python那样，支持函数默认参数。所以使用Go时，我们需要一种方便、通用的手法来完成这件事。
//
//Go的很多开源项目都使用Option模式，但各自的实现可能有些许细微差别。
// @See https://pengrl.com/p/60015/

type Foo struct {
	Num int
	Str string
	Key string
}

type ModOption func(*Foo)

func New(key string, modOptions ...ModOption) *Foo {
	// 可选，默认值设置
	foo := Foo{
		Num: 100,
		Str: "hello",
		Key: key,
	}

	// option 选项模式的核心：多个withxxx 方法 修改对象的字段
	for _, fn := range modOptions {
		fn(&foo)
	}

	return &foo
}

// Golang - 选项模式 vs 构建器模式 https://blog.csdn.net/csjds/article/details/131011093
// func Builder()

func (f *Foo) debugPrint() {
	fmt.Printf("%#v\n", f)
	bytes, _ := json.Marshal(f)
	str := string(bytes)
	fmt.Printf("%v\n", str)
}
func WithNum(num int) ModOption {
	return func(option *Foo) {
		option.Num = num
	}
}
func WithStr(str string) ModOption {
	fmt.Println(str)
	return func(option *Foo) {
		option.Str = str
	}
}

func main() {
	New("#1", func(option *Foo) {
		// 调用方只设置 num
		option.Num = 200
	}).debugPrint()
	New("#2", WithNum(23), WithStr("33")).debugPrint()
	New("#3", WithStr("44")).debugPrint()
}
